import { useEffect, useRef } from "react";
import useSWR from "swr";
import type {
  AgentPresenceRecord,
  AgentSession,
  AgentSummary,
  EventRecord,
  EnvironmentView,
  MemoryRecord,
  GovernanceTask,
  LedgerSnapshot,
  PresenceState,
  TimelineRecord
} from "@agent-society/types";

const API_BASE = process.env.NEXT_PUBLIC_API_BASE ?? "http://localhost:8080";

function authHeaders() {
  if (typeof window === "undefined") return {};
  const token = window.localStorage.getItem("agent-society-token");
  return token ? { Authorization: `Bearer ${token}` } : {};
}

async function fetchJSON<T>(path: string): Promise<T> {
  const res = await fetch(path, {
    cache: "no-store",
    headers: {
      "Content-Type": "application/json",
      ...authHeaders()
    }
  }).catch(() => null);
  if (!res || !res.ok) {
    throw new Error(`Request failed for ${path}`);
  }
  return res.json() as Promise<T>;
}

async function mutateJSON<T>(path: string, init: RequestInit): Promise<T> {
  const res = await fetch(path, {
    ...init,
    headers: {
      "Content-Type": "application/json",
      ...authHeaders(),
      ...(init.headers ?? {})
    }
  }).catch(() => null);
  if (!res || !res.ok) {
    throw new Error(`Request failed for ${path}`);
  }
  return res.json() as Promise<T>;
}

export function useLedgerSnapshot(initial: LedgerSnapshot) {
  return useSWR<LedgerSnapshot>(`${API_BASE}/telemetry/ledger-summary`, fetchJSON, {
    fallbackData: initial,
    refreshInterval: 10_000,
    shouldRetryOnError: false
  });
}

export function useAgents(initial: AgentSummary[]) {
  return useSWR<AgentSummary[]>(`${API_BASE}/agents/summary`, fetchJSON, {
    fallbackData: initial,
    refreshInterval: 8_000,
    shouldRetryOnError: false
  });
}

export function useEvents(initial: EventRecord[]) {
  return useSWR<EventRecord[]>(`${API_BASE}/events/feed?limit=50`, fetchJSON, {
    fallbackData: initial,
    refreshInterval: 5_000,
    shouldRetryOnError: false
  });
}

export function useTasks(initial: GovernanceTask[]) {
  return useSWR<GovernanceTask[]>(`${API_BASE}/governance/tasks`, fetchJSON, {
    fallbackData: initial,
    refreshInterval: 12_000,
    shouldRetryOnError: false
  });
}

export function usePresence(initial?: AgentPresenceRecord[]) {
  return useSWR<AgentPresenceRecord[]>(`${API_BASE}/agents/presence`, fetchJSON, {
    fallbackData: initial,
    refreshInterval: 8_000,
    shouldRetryOnError: false
  });
}

function timelineKey(entry: TimelineRecord) {
  return `${entry.timestamp}|${entry.agent}|${entry.type}|${entry.prompt ?? ""}`;
}

function mergeTimelineEntries(existing: TimelineRecord[], incoming: TimelineRecord[], limit: number) {
  const merged = [...existing];
  for (const entry of incoming) {
    const key = timelineKey(entry);
    const idx = merged.findIndex(item => timelineKey(item) === key);
    if (idx >= 0) {
      merged[idx] = entry;
    } else {
      merged.push(entry);
    }
  }
  merged.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
  if (merged.length > limit) {
    return merged.slice(0, limit);
  }
  return merged;
}

export function useTimeline(initial: TimelineRecord[] = [], limit = 100) {
  const key = `${API_BASE}/timeline?limit=${limit}`;
  const swr = useSWR<TimelineRecord[]>(key, fetchJSON, {
    fallbackData: initial,
    refreshInterval: 30_000,
    shouldRetryOnError: false
  });

  const seenRef = useRef<Set<string>>();
  if (!seenRef.current && Array.isArray(swr.data)) {
    seenRef.current = new Set(swr.data.map(item => timelineKey(item)));
  }

  useEffect(() => {
    if (typeof window === "undefined") return undefined;
    const source = new EventSource(`${API_BASE}/timeline/stream?limit=${limit}`);
    source.onmessage = event => {
      try {
        const payload = JSON.parse(event.data) as TimelineRecord | TimelineRecord[];
        const entries = Array.isArray(payload) ? payload : [payload];
        const seen = seenRef.current ?? new Set<string>();
        const fresh: TimelineRecord[] = [];
        for (const entry of entries) {
          const keyValue = timelineKey(entry);
          if (seen.has(keyValue)) continue;
          seen.add(keyValue);
          fresh.push(entry);
        }
        if (fresh.length > 0) {
          swr.mutate(current => mergeTimelineEntries(current ?? [], fresh, limit), false);
        }
        seenRef.current = seen;
      } catch (err) {
        console.warn("timeline stream parse error", err);
      }
    };
    source.onerror = () => {
      source.close();
    };
    return () => {
      source.close();
    };
  }, [limit]);

  return swr;
}

export function createAgentSession(agentId: string, client: string, capabilities: string[] = []) {
  return mutateJSON<AgentSession>(`${API_BASE}/agents/session`, {
    method: "POST",
    body: JSON.stringify({ agentId, client, capabilities })
  });
}

export function heartbeatAgentSession(
  sessionId: string,
  state: PresenceState = "online",
  capabilities?: string[]
) {
  return mutateJSON<AgentSession>(`${API_BASE}/agents/session/${sessionId}/heartbeat`, {
    method: "POST",
    body: JSON.stringify({ state, capabilities })
  });
}

export function closeAgentSession(sessionId: string) {
  return mutateJSON<AgentPresenceRecord>(`${API_BASE}/agents/session/${sessionId}`, {
    method: "DELETE"
  });
}

export function fetchAgentMemories(agentId: string, limit = 10, query?: string) {
  const url = new URL(`${API_BASE}/agents/${agentId}/memories`);
  url.searchParams.set("limit", String(limit));
  if (query) {
    url.searchParams.set("query", query);
  }
  return fetchJSON<MemoryRecord[]>(url.toString());
}

export function useAgentMemories(agentId: string, limit = 10, query?: string, initial?: MemoryRecord[]) {
  const key = `${API_BASE}/agents/${agentId}/memories?limit=${limit}${query ? `&query=${encodeURIComponent(query)}` : ""}`;
  return useSWR<MemoryRecord[]>(key, fetchJSON, {
    fallbackData: initial,
    refreshInterval: 15_000,
    shouldRetryOnError: false
  });
}

export function fetchEnvironmentTree(limit = 5) {
  const url = `${API_BASE}/environment/tree?limit=${limit}`;
  return fetchJSON<EnvironmentView>(url);
}

export function useEnvironmentTree(limit = 5, initial?: EnvironmentView) {
  const key = `${API_BASE}/environment/tree?limit=${limit}`;
  return useSWR<EnvironmentView>(key, fetchJSON, {
    fallbackData: initial,
    refreshInterval: 30_000,
    shouldRetryOnError: false
  });
}
